import {
  defineComponent,
  PropType,
  computed,
  ref
} from 'vue';
import { useModel } from '@/utils/useModel';
import { VisualEditorModel, VisualEditorConfig, VisualEditorComponent, createBlock, BlockModel } from '@/model/model';
import { VisualEditorBlock } from '@/components/visual-editor-block/index';
import './index.scss';

export const VisualEditor = defineComponent({
  components: {
    VisualEditorBlock
  },
  props: {
    modelValue: {
      type: Object as PropType<VisualEditorModel>,
    },
    config: {
      type: Object as PropType<VisualEditorConfig>
    }
  },
  emits: {
    'update.modelValue': (val?: VisualEditorModel) => true,
  },
  setup(props, context) {
    const model = useModel(() => props.modelValue, (value) => {
      context.emit('update.modelValue', value);
    });
    console.log(model);
    const contextStyles = computed(() => ({
      width: `${props.modelValue?.container.width}px`,
      height: `${props.modelValue?.container.height}px`
    }));
    const contextRef = ref({} as HTMLElement);
    console.log('componentList', props.config?.componentList);

    const focusData = computed(() => {
      const focus: BlockModel[] =
        model.value?.blocks && model.value?.blocks.filter((v) => v.focus) || [];
      const unfocus: BlockModel[] =
        model.value?.blocks && model.value?.blocks.filter((v) => !v.focus) || [];
      return {
        focus,
        unfocus
      }
    })

    const methods = {
      clearFocus: (block?: BlockModel) => {
        let blocks = model.value?.blocks || [];
        if (blocks.length === 0) return;

        if (block) {
          blocks = blocks.filter((v) => v !== block);
        }
        blocks.forEach((block) => (block.focus = false));
      },
    }

    const menuDragger = () => {
      let component = null as null | VisualEditorComponent;
      const contextHandler = {
        dragenter: (event: DragEvent) => {
          event.dataTransfer!.dropEffect = 'move';
        },
        dragover: (event: DragEvent) => {
          event.preventDefault();
        },
        dragleave: (event: DragEvent) => {
          event.dataTransfer!.dropEffect = 'none';
        },
        drop: (event: DragEvent) => {
          const blocks = model.value?.blocks || [];
          blocks.push(
            createBlock({
              component: component,
              top: event.offsetY,
              left: event.offsetX
            })
          );
          model.value = {
            ...model.value,
            blocks,
          } as VisualEditorModel
        }

      }

      const blockHandler = {
        dragstart: (event: DragEvent, current: VisualEditorComponent) => {
          contextRef.value.addEventListener('dragenter', contextHandler.dragenter);
          contextRef.value.addEventListener('dragover', contextHandler.dragover);
          contextRef.value.addEventListener('dragleave', contextHandler.dragleave);
          contextRef.value.addEventListener('drop', contextHandler.drop);
          component = current;
        },
        dragend: () => {
          contextRef.value.removeEventListener('dragenter', contextHandler.dragenter);
          contextRef.value.removeEventListener('dragover', contextHandler.dragover);
          contextRef.value.removeEventListener('dragleave', contextHandler.dragleave);
          contextRef.value.removeEventListener('drop', contextHandler.drop);
          component = null;
        },

      }

      return blockHandler
    }

    const blockDragger = (() => {
      let dragState = {
        startX: 0,
        startY: 0,
        startPos: [] as { left: number; top: number }[],
      };

      const mousemove = (e: MouseEvent) => {
        const durX = e.clientX - dragState.startX;
        const durY = e.clientY - dragState.startY;
        focusData.value.focus.forEach((block, i) => {
          block.top = dragState.startPos[i].top + durY;
          block.left = dragState.startPos[i].left + durX;
        });
      };
      const mouseup = (e: MouseEvent) => {
        document.removeEventListener("mousemove", mousemove);
        document.removeEventListener("mouseup", mouseup);
      };

      const mousedown = (e: MouseEvent) => {
        dragState = {
          startX: e.clientX,
          startY: e.clientY,
          startPos: focusData.value.focus.map(({ top, left }) => ({
            top,
            left,
          })),
        };
        document.addEventListener("mousemove", mousemove);
        document.addEventListener("mouseup", mouseup);
      };

      return { mousedown };
    })();

    // 处理组件的选中状态
    const focusHandler = (() => {
      return {
        container: {
          onMousedown: (e: MouseEvent) => {
            e.stopPropagation();
            methods.clearFocus();
          },
        },
        block: {
          onMousedown: (e: MouseEvent, block: BlockModel) => {
            e.stopPropagation();
            e.preventDefault();
            // 只有元素未选中状态下， 才去处理
            if (!block.focus) {
              if (!e.shiftKey) {
                block.focus = !block.focus;
                methods.clearFocus(block);
              } else {
                block.focus = true;
              }
            }
            // 处理组件的选中移动
            blockDragger.mousedown(e);
          },
        },
      };
    })();


    return () => (
      <div class="editor-container">
        <div class="editor-menu">
          {props.config?.componentList.map((component) => (
            <div class="menu-item">
              <span class="menu-item-label">{component.label}</span>
              {component.preview()}
            </div>
          ))}
        </div>
        <div class="editor-body">
          <div class="editor-header center-x"></div>
          <div class="editor-context center" ref={contextRef} style={contextStyles.value} {...focusHandler.container}>
            {
              !!model.value && !!model.value.blocks && (
                model.value.blocks.map((block, index) => (
                  <VisualEditorBlock block={block} key={`block_${index}`} config={props.config} {...{
                    onMousedown: (e: MouseEvent) =>
                      focusHandler.block.onMousedown(e, block),
                  }} />
                ))
              )
            }
          </div>
        </div>
        <div class="editor-operator"></div>
      </div>
    );
  }
});
